package client;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.net.SocketException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Thread reading from an InputStream and writing to stdout.
 * If a Users logs in, a UDPListenerThread gets started.
 */
public class TCPListenerThread
        implements Runnable,Stoppable
{
	private static String IN_LOGGED_IN="!logged-in";
	private static String OUT_LOGGED_IN="Successfully logged in as %s!";
	private static String IN_LOGGED_OUT="!logged-out";
	private static String OUT_LOGGED_OUT="Successfully logged out as %s!";
	
	
   private InputStream is;
   private BufferedReader br;
   private Stoppable parent;
   private int udpPort;
   private UDPListenerThread udpListener;
   private ExecutorService executor=null;
   private boolean bStopped=false;

   /**
    * Constructor
    * @param in InputStream, the Thread reads from this InputStream until it is closed
    * and writes to stdout;
    * @param parent Stoppable, if the socket/inputstream in gets closed, a stop method on this object shall be called
    */
   public TCPListenerThread(InputStream in,Stoppable parent,int udpPort)
   {
       this.is=in;
       this.parent=parent;
       this.udpPort=udpPort;
       executor=Executors.newFixedThreadPool(1);
   }
   
   @Override
   public void run()
   {
       try
       {
           br = new BufferedReader(new InputStreamReader(is));
           String line=null;
           while ( (line = br.readLine()) != null)
           {
        	   System.out.println(processInput(line));
           }
           System.out.println("TCPListenerThread loop ended");
       }
       catch (IOException ioe)
       {
    	   System.out.println("IOException occurred while listening for Messages from the Server "+ioe.getMessage());
       }
       finally
       {
    	   this.stop();
    	   System.out.println("StreamReaderThread stops..."); 
       }
   }
   
	/**
	 * Processes the received machine-readable-tcp-message-texts and transforms them to user-readable-texts.
	 * If a login happened successfully an UDPListenerThread gets started.
	 * If a logout happened successfully the UDPListenerThread gets stopped.
	 * @param input String machine-readable-notification-text
	 * @return String user-readable-text
	 */   
   public String processInput(String input)
   {
	   if(input.startsWith(IN_LOGGED_IN))
	   {
		   String user=input.substring(IN_LOGGED_IN.length());
		   try
		   {
	    	   udpListener=new UDPListenerThread(udpPort,user);
	    	   executor.execute(udpListener);
		   }
		   catch(SocketException ex)
		   {
			   System.out.println("UDP-Socket could not be opened, or couldn't be bound to port: "+udpPort+"!");
		   }
		   return String.format(OUT_LOGGED_IN, user);
	   }
	   else if(input.startsWith(IN_LOGGED_OUT))
	   {
		   String user=input.substring(IN_LOGGED_OUT.length());
		   if(udpListener!=null)
		   {
			   udpListener.stop();
		   }
		   return String.format(OUT_LOGGED_OUT, user);
	   }
	   else return input;
   }
   
   @Override
   public synchronized void stop()
   {
	   if(!bStopped)
	   {
		   bStopped=true;
	   	   try 
		   {
			   if(br!=null)
			   {
				   br.close();
			   }
		   }
		   catch (IOException e) 
		   {
			//ignore
		   }
		   try 
		   {
			   is.close();
		   }
		   catch (IOException e) 
		   {
			//ignore
		   }    	   
		   if(udpListener!=null)
		   {
			   udpListener.stop();
		   }
		   executor.shutdown();
		   executor.shutdownNow();
		   parent.stop();
	   }
   }
}
